package com.wy.panda.exception;

import java.util.List;
import java.util.Set;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.Valid;

import org.springframework.validation.BindException;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;

import com.wy.panda.common.model.R;
import com.wy.panda.common.util.CollectionUtils;
import com.wy.panda.common.util.StringUtils;

import lombok.extern.slf4j.Slf4j;

/**
 * 系统层级最外层的异常处理器
 * 
 * @author wangyuan
 * @since 2019年6月7日 上午4:26:24
 *
 */
@Slf4j
@RestControllerAdvice
public class PandaExceptionHandler {
    
    private static final String COMMON_ERROR_MESSAGE = "您的请求被外星人劫持了，请联系系统管理员！";
    
    /**
     * 控制层BEAN VALIDATION异常
     */
    @ExceptionHandler(ConstraintViolationException.class)
    @ResponseStatus
    public  R<?> resolveConstraintViolationException(ConstraintViolationException ex) {
        //log.error("BEAN验证失败", ex);
        Set<ConstraintViolation<?>> constraintViolations = ex.getConstraintViolations();
        if(!CollectionUtils.isEmpty(constraintViolations)){
            StringBuilder msgBuilder = new StringBuilder();
            for(ConstraintViolation<?> constraintViolation :constraintViolations){
                msgBuilder.append(constraintViolation.getMessage()).append(",");
            }
            String errorMessage = msgBuilder.toString();
            if(errorMessage.length()>1){
                errorMessage = errorMessage.substring(0,errorMessage.length()-1);
            }
            return R.error(errorMessage);
        }
        return R.error(ex.getMessage());
    }
    
    /**
     * bean对象里参数校验， {@link Valid}验证失败异常
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus
    public R<?> resolveMethodArgumentNotValidException(MethodArgumentNotValidException ex) {
        log.error("BEAN对象中参数校验失败", ex);
        List<ObjectError>  objectErrors = ex.getBindingResult().getAllErrors();
        if(!CollectionUtils.isEmpty(objectErrors)) {
            StringBuilder msgBuilder = new StringBuilder();
            for (ObjectError objectError : objectErrors) {
                msgBuilder.append(objectError.getDefaultMessage()).append(",");
            }
            String errorMessage = msgBuilder.toString();
            if (errorMessage.length() > 1) {
                errorMessage = errorMessage.substring(0, errorMessage.length() - 1);
            }
            return R.error(errorMessage);
        }
        return R.error(ex.getMessage());
    }
    
    /**
     * 参数绑定错误
     */
    @ExceptionHandler(BindException.class)
    @ResponseStatus
    public R<?> handleBindException(BindException e) {
        List<ObjectError> allErrors = e.getAllErrors();
        if(CollectionUtils.isEmpty(allErrors)) {
            return R.error(COMMON_ERROR_MESSAGE);
        }
        StringBuilder errorMessageSb = new StringBuilder();
        allErrors.forEach(item -> {
            if(StringUtils.isBlank(item.getDefaultMessage())) {
                return;
            }
            
            errorMessageSb.append(item.getDefaultMessage());
            errorMessageSb.append(";");
        });
        
        if(errorMessageSb.length() == 0) {
            errorMessageSb.append(COMMON_ERROR_MESSAGE);
        }
        
        return R.error(errorMessageSb.toString());
    }
    
    /**
     * 控制层方法入参必有的异常捕获处理
     */
    @ExceptionHandler({ MissingServletRequestParameterException.class })
    @ResponseStatus
    public R<?> missingServletRequestParameterException(MissingServletRequestParameterException ex) {
        return R.error(ex.getParameterName() + "不能为空");
    }
    
    /**
     * 类型转换异常拦截
     */
    @ExceptionHandler(MethodArgumentTypeMismatchException.class)
    @ResponseStatus
    public R<?> handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e) {
        return R.error("类型转换错误");
    }
    
    /**
     * 自定义已知异常拦截
     */
    @ExceptionHandler(PandaException.class)
    @ResponseStatus
    public R<?> handlePandaException(PandaException e) {
        return R.error(e.getMessage());
    }
    
    /**
     * 通用异常拦截
     */
    @ExceptionHandler(Exception.class)
    @ResponseStatus
    public R<?> handleException(Exception e) {
        log.error("系统错误：", e);
        return R.error(COMMON_ERROR_MESSAGE);
    }
}
